package org.wheel.plugins.toolkit.sql.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.Setter;
import org.wheel.plugins.toolkit.jsqlparse.helper.ParamValueConvert;
import org.wheel.plugins.toolkit.sql.entity.BeanMeta;
import org.wheel.plugins.toolkit.sql.enums.Dialect;
import org.wheel.plugins.toolkit.sql.enums.SqlModel;
import org.wheel.plugins.toolkit.sql.helper.FieldMeta;
import org.wheel.plugins.toolkit.sql.helper.LambdaUtils;
import org.wheel.plugins.toolkit.sql.helper.SFunction;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Lambda 风格的 Insert 包装器
 * @param <T> 实体类型
 */
@Getter
public class LambdaInsertWrapper<T> {

    private Class<T> entityClass;
    private LinkedHashMap<String, InsertParam> insertValues; // 存储要插入的字段和值
    private List<FieldMeta> fields; // 实体的字段列表
    private String tableName;
    private boolean useGeneratedKeys = false; // 是否使用自动生成的主键
    private final SqlModel sqlModel = SqlModel.getDefaultModel();
    @Setter
    protected Dialect dialect;

    public LambdaInsertWrapper() {
        this.insertValues = new LinkedHashMap<>(); // 保持插入顺序
        this.dialect = GlobalConfig.getDialect();
    }

    public LambdaInsertWrapper(Class<T> entityClass) {
        this();
        if (this.entityClass == null){
            this.entityClass = entityClass;
            this.tableName = BeanMeta.of(entityClass).getFullTableName();
            // 反射获取实体的字段和值
            this.fields = BeanMeta.of(entityClass).getFields();
        }
    }
    // 设置方言
    public LambdaInsertWrapper<T> dialect(Dialect dialect) {
        this.dialect = dialect;
        return this;
    }
    /**
     * 设置要插入的实体
     * @param entity 实体对象
     * @return 当前包装器
     */
    public LambdaInsertWrapper<T> insert(T entity) {
        if (this.entityClass == null && entity != null) {
            this.entityClass = (Class<T>) entity.getClass();
            this.tableName = BeanMeta.of(entityClass).getFullTableName();
            // 反射获取实体的字段和值
            this.fields = BeanMeta.of(entityClass).getFields();
            Map<String, InsertParam> fieldValues = getFieldValues(entity, true);
            this.insertValues.putAll(fieldValues);
        } else if(insertValues.isEmpty() && entity != null){
            Map<String, InsertParam> fieldValues = getFieldValues(entity, true);
            this.insertValues.putAll(fieldValues);
        }
        return this;
    }

    /**
     * 设置单个字段的值
     * @param column 字段的 Lambda 引用 (如 User::getName)
     * @param value 值
     * @return 当前包装器
     */
    public LambdaInsertWrapper<T> set(SFunction<T, ?> column, Object value) {
        if (column != null && entityClass != null) {
            // 将 Lambda 函数转换为数据库字段名
            String fieldName = LambdaUtils.getFieldName(column);
            fields.stream().filter(field -> field.getFieldName().equals(fieldName)).findAny()
                    .ifPresent(field -> {
                        String columnName = field.getColumn();
                        Class<?> fieldType = LambdaUtils.getFieldType(entityClass, column);
                        this.insertValues.put(columnName, new InsertParam(value, fieldType));
                    });
        } else if (column != null) {
            // 如果 entityClass 未知，使用 Object 类型（最不精确）
            String columnName = LambdaUtils.getColumn(column);
            this.insertValues.put(columnName, new InsertParam(value, Object.class));
        }

        return this;
    }

    public LambdaInsertWrapper<T> set(String column, Object value){
        if(ObjectUtil.isNotNull(value)) {
            this.insertValues.put(column, new InsertParam(value, value.getClass()));
        }
        return this;
    }

    /**
     * 指定表名
     * @param tableName 表名
     * @return 当前包装器
     */
    public LambdaInsertWrapper<T> into(String tableName) {
        this.tableName = tableName;
        return this;
    }

    /**
     * 声明使用自动生成的主键
     * @return 当前包装器
     */
    public LambdaInsertWrapper<T> useGeneratedKeys() {
        this.useGeneratedKeys = true;
        return this;
    }

    // --- Getter Methods ---

//    public Class<T> getEntityClass() {
//        return entityClass;
//    }
//
//    public Map<String, Object> getInsertValues() {
//        return insertValues;
//    }
//
//    public String getTableName() {
//        return tableName;
//    }
//
//    public boolean isUseGeneratedKeys() {
//        return useGeneratedKeys;
//    }

    /**
     * 获取 INSERT SQL 语句（由 LambdaSqlResolver 调用）
     * @return SQL 语句
     */
    public String getSql() {
        if (insertValues.isEmpty()) {
            throw new RuntimeException("Insert values cannot be empty.");
        }

        StringBuilder sql = new StringBuilder("INSERT INTO ");

        // 如果指定了表名，使用指定的；否则根据实体类推断
        if (tableName != null && !tableName.isEmpty()) {
            sql.append(tableName);
        } else if (entityClass != null) {
            sql.append(BeanMeta.of(entityClass).getFullTableName());
        } else {
            throw new RuntimeException("Table name not specified and cannot be inferred from entity.");
        }

        sql.append(" (");

        List<String> columns = insertValues.keySet().stream().toList();
        sql.append(columns.stream().map(column -> quoteIdentifier(column, this.dialect)).collect(Collectors.joining(", ")));
        sql.append(") VALUES (");

        // 拼接字段名
//        String placeholders = columns.stream().map(column -> new JdbcNamedParameter(column).toString()).collect(Collectors.joining(", "));
//        String placeholders = columns.stream().map(column -> "?").collect(Collectors.joining(", "));
        String placeholders = getPlaceholders();
        sql.append(placeholders);

        sql.append(")");

        return sql.toString();
    }

    private String quoteIdentifier(String identifier, Dialect dialect) {
        return switch (dialect) {
            case MYSQL -> "`" + identifier + "`";
            case SQLSERVER, SQLSERVER2012 -> "[" + identifier + "]";
            case ORACLE -> "\"" + identifier + "\"";
//            default -> throw new UnsupportedOperationException("Unsupported dialect: " + dialect);
            default -> identifier;
        };
    }

    public String getPlaceholders() {
        return switch (sqlModel){
            case PROPERTY_NAME -> insertValues.keySet().stream().map(column -> ":" + column).collect(Collectors.joining(", "));
            case NUMBER -> insertValues.keySet().stream().map(column -> "$" + column).collect(Collectors.joining(", "));
            case NATIVE -> insertValues.keySet().stream().map(column -> "?").collect(Collectors.joining(", "));
            case MYBATIS -> insertValues.keySet().stream().map(column -> "#{" + column + "}").collect(Collectors.joining(", "));
            case DIRECT_SQL -> insertValues.keySet().stream().map(column -> ParamValueConvert.toLiteral(insertValues.get(column), GlobalConfig.getDialect())).collect(Collectors.joining(", "));
            case CUSTOM_0 -> insertValues.keySet().stream().map(column -> "{" + column + "}").collect(Collectors.joining(", "));
            case CUSTOM_1 -> insertValues.keySet().stream().map(column -> "${" + column + "}").collect(Collectors.joining(", "));
            default -> throw new RuntimeException("Invalid SQL model.");
        };
    }

    /**
     * 获取参数映射
     * @return 参数 LinkedHashMap
     */
    public LinkedHashMap<String, Object> getParamMap() {
        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
        for (Map.Entry<String, InsertParam> entry : insertValues.entrySet()) {
            map.put(entry.getKey(), entry.getValue().getValue());
        }
        return map;
    }
    /**
     * 获取参数映射（包含值和类型）
     * @return 参数 LinkedHashMap
     */
    public LinkedHashMap<String, InsertParam> getFullParamMap() {
        return new LinkedHashMap<>(insertValues);
    }

    /**
     * 获取实体对象的所有字段值
     * @param entity 实体对象
     * @return 字段名 -> 值的映射
     */
    private Map<String, InsertParam> getFieldValues(Object entity, boolean ignoreNull){
        Map<String, InsertParam> fieldValues = new HashMap<>();
        if (entity == null) {
            return fieldValues;
        }

        Class<?> clazz = entity.getClass();
        Field[] fields = clazz.getDeclaredFields();

        List<FieldMeta> fieldMetas = BeanMeta.of(clazz).getFields();

        for (Field field : fields) {
            field.setAccessible(true); // 允许访问 private 字段
            try {
                Object value = field.get(entity);
                if(ignoreNull && value == null){
                    continue;
                }
                Class<?> type = field.getType(); // 获取字段类型
                // 使用字段名作为数据库列名（生产环境可能需要映射）
                if(CollUtil.isNotEmpty(fieldMetas)) {
                    fieldMetas.stream().filter(f -> f.getFieldName().equals(field.getName()))
                            .findAny()
                            .ifPresent(f -> {
                                fieldValues.put(f.getColumn(), new LambdaInsertWrapper.InsertParam(value, type));
                            });
                }else{
                    fieldValues.put(StrUtil.toUnderlineCase(field.getName()), new LambdaInsertWrapper.InsertParam(value, type));
                }
            } catch (IllegalAccessException e) {
                // 忽略无法访问的字段
                e.printStackTrace();
            }
        }
        return fieldValues;
    }


    // 内部类，用于存储参数值和类型
    public static class InsertParam {
        /**
         * 参数值
         */
        private final Object value;
        /**
         * 参数类型
         */
        private final Class<?> fieldType;

        public InsertParam(Object value, Class<?> fieldType) {
            this.value = value;
            this.fieldType = fieldType;
        }

        public Object getValue() {
            return value;
        }

        public Class<?> getFieldType() {
            return fieldType;
        }
    }
}
